﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

using SpotItComponents;
using SpotItServerAPI;

using Vitruvian.Serialization;
using Vitruvian.Services;
using Vitruvian.Testing;
using Vitruvian.Logging;

namespace SpotItServerTester
{
    [ContainsTests("SystemTest.AccountManagement")]
    public class UserAccountTester : BaseTester
    {
        public UserAccountTester() { }

        [OneTimeSetup]
        public void TestSuiteSetup()
        {
            base.EstablishConnectionToServer();

            base.WaitForAllDistributedServices();
            Assert.IsNotNull(accountManager);
            Assert.IsNotNull(symbolManager);

            base.LoginInAsAdmin();
            Assert.IsNotNull(mySession);
            Assert.IsTrue(mySession.UserInfo.IsAdmin);

            // Remove all user accounts, except the admin account
            // NOTE: THIS DELETES ALL EXISTING USER ACCOUNTS AND RE-ADDS THE DEFAULT ADMIN ACCOUNT
            OperationResult result = accountManager.RemoveAllUserAccounts(mySession);
            Assert.IsTrue(result.Success);
        }

        [Test(1)]
        public void TestPlayerRegisteration()
        {
            // Perform a typical player registeration.
            // This will create a user account for the new player and will log the player in.
            OperationResult result = accountManager.RegisterPlayer("Joe", "test", "Joe Jones", "joe@gmail.com");
            Assert.IsTrue(result.Success);
            Session session1 = result.Data as Session;
            Assert.IsNotNull(session1);
            Assert.IsEqual("Joe", session1.UserInfo.Username);
            Assert.IsEqual("Joe Jones", session1.UserInfo.FullName);
            Assert.IsFalse(session1.UserInfo.IsAdmin);
            Assert.IsFalse(session1.UserInfo.IsDesigner);

            // Register another user account, but without an email account
            // This should work because the email account is optional
            result = accountManager.RegisterPlayer("Jane", "test", "Jane Johnson", "");
            Assert.IsTrue(result.Success);
            Session session2 = result.Data as Session;
            Assert.IsNotNull(session2);
            Assert.IsEqual("Jane", session2.UserInfo.Username);
            Assert.IsEqual("Jane Johnson", session2.UserInfo.FullName);
            Assert.IsFalse(session2.UserInfo.IsAdmin);
            Assert.IsFalse(session2.UserInfo.IsDesigner);

            // Try to register another user account, but without a full name
            // This should not work.
            result = accountManager.RegisterPlayer("Max", "test", "", "");
            Assert.IsFalse(result.Success);

            // Try to register another user account, but without a password
            // This should not work.
            result = accountManager.RegisterPlayer("Max", "", "Matthews", "");
            Assert.IsFalse(result.Success);

            // Try to register another user account, but without a first name
            // This should not work.
            result = accountManager.RegisterPlayer("", "test", "Matthews", "");
            Assert.IsFalse(result.Success);

            // Logout the sessions. This should work and therefore return a true
            result = accountManager.Logout(session1);
            Assert.IsTrue(result.Success);
            result = accountManager.Logout(session2);
            Assert.IsTrue(result.Success);

            // Try to the logout the sessions again.  This shouldn't work and therefore return a false
            Assert.IsFalse(accountManager.Logout(session1).Success);
            Assert.IsFalse(accountManager.Logout(session2).Success);
        }

        [Test(2)]
        public void TestDesignerRegisterationAndLogout()
        {
            // Perform a typical designer registeration.
            // This will create a user account for the new player and will log the player in.
            OperationResult result = accountManager.RegisterDesigner("Dan", "test", "Dan Jones", "dan@gmail.com");
            Session session1 = result.Data as Session;
            Assert.IsNotNull(session1);
            Assert.IsEqual("Dan", session1.UserInfo.Username);
            Assert.IsEqual("Dan Jones", session1.UserInfo.FullName);
            Assert.IsFalse(session1.UserInfo.IsAdmin);
            Assert.IsTrue(session1.UserInfo.IsDesigner);

            // Register another user account, but without an email account
            // This should work because the email account is optional
            result = accountManager.RegisterDesigner("Mary", "test", "Mary Johnson", "");
            Session session2 = result.Data as Session;
            Assert.IsNotNull(session2);
            Assert.IsEqual("Mary", session2.UserInfo.Username);
            Assert.IsEqual("Mary Johnson", session2.UserInfo.FullName);
            Assert.IsFalse(session2.UserInfo.IsAdmin);
            Assert.IsTrue(session2.UserInfo.IsDesigner);

            // Try to register another user account, but without a full name
            // This should not work.
            result = accountManager.RegisterDesigner("Max", "test", "", "");
            Assert.IsFalse(result.Success);

            // Try to register another user account, but without a password
            // This should not work.
            result = accountManager.RegisterDesigner("Max", "", "Matthews", "");
            Assert.IsFalse(result.Success);

            // Try to register another user account, but without a first name
            // This should not work.
            result = accountManager.RegisterDesigner("", "test", "Matthews", "");
            Assert.IsFalse(result.Success);

            // Logout the sessions. This should work and therefore return a true
            Assert.IsTrue(accountManager.Logout(session1).Success);
            Assert.IsTrue(accountManager.Logout(session2).Success);

            // Try to the logout the sessions again.  This shouldn't work and therefore return a false
            Assert.IsFalse(accountManager.Logout(session1).Success);
            Assert.IsFalse(accountManager.Logout(session2).Success);

            // Try to logout a null sesion
            Assert.IsFalse(accountManager.Logout(null).Success);
        }

        [Test(3)]
        public void TestUserLoginAndLogout()
        {
            // Log in as Dan
            OperationResult result = accountManager.Login("Dan", "test");
            Assert.IsTrue(result.Success);
            Session session1 = result.Data as Session;
            Assert.IsNotNull(session1);
            Assert.IsEqual("Dan", session1.UserInfo.Username);
            Assert.IsEqual("Dan Jones", session1.UserInfo.FullName);
            Assert.IsFalse(session1.UserInfo.IsAdmin);
            Assert.IsTrue(session1.UserInfo.IsDesigner);

            // Log in as Jane
            result = accountManager.Login("Jane", "test");
            Assert.IsTrue(result.Success);
            Session session2 = result.Data as Session;
            Assert.IsNotNull(session2);
            Assert.IsEqual("Jane", session2.UserInfo.Username);
            Assert.IsEqual("Jane Johnson", session2.UserInfo.FullName);
            Assert.IsFalse(session2.UserInfo.IsAdmin);
            Assert.IsFalse(session2.UserInfo.IsDesigner);

            // Try to log in as Max, but since there isn't Max account, no session should be returned
            result = accountManager.Login("Max", "test");
            Assert.IsFalse(result.Success);

            // Logout the sessions. This should work and therefore return a true
            Assert.IsTrue(accountManager.Logout(session1).Success);
            Assert.IsTrue(accountManager.Logout(session2).Success);

            // Try to the logout the sessions again.  This shouldn't work and therefore return a false
            Assert.IsFalse(accountManager.Logout(session1).Success);
            Assert.IsFalse(accountManager.Logout(session2).Success);
        }

        [Test(4)]
        public void TestAnnonymousLoginAndLogout()
        {
            // Log a player in with a name, but without an account
            OperationResult result = accountManager.LoginPlayer("Frank Flowers");
            Assert.IsTrue(result.Success);
            Session session1 = result.Data as Session;
            Assert.IsNotNull(session1);
            Assert.IsEqual("Frank Flowers", session1.UserInfo.Username);
            Assert.IsEqual("Frank Flowers", session1.UserInfo.FullName);
            Assert.IsFalse(session1.UserInfo.IsAdmin);
            Assert.IsFalse(session1.UserInfo.IsDesigner);

            // Logout the session. This should work and therefore return a true
            Assert.IsTrue(accountManager.Logout(session1).Success);

            // Try to the logout the session again.  This shouldn't work and therefore return a false
            Assert.IsFalse(accountManager.Logout(session1).Success);

            // Log a player in without a name.
            // This should not work.
            result = accountManager.LoginPlayer("");
            Assert.IsFalse(result.Success);

        }


        [Test(6)]
        public void TestAdminUserAccountLookup()
        {
            // Try looking up a user named "Dan", using an admin session
            // An account should exist because we added in previous test case
            OperationResult result = accountManager.Lookup(mySession, "Dan");
            Assert.IsTrue(result.Success);
            UserAccount account1 = result.Data as UserAccount;
            Assert.IsNotNull(account1);
            Assert.IsEqual("Dan", account1.Username);

            // Try looking up a user named "Joe", using an admin session
            // An account should exist because we added in previous test case
            result = accountManager.Lookup(mySession, "Joe");
            Assert.IsTrue(result.Success);
            UserAccount account2 = result.Data as UserAccount;
            Assert.IsNotNull(account2);
            Assert.IsEqual("Joe", account2.Username);

            // Log in as Dan
            result = accountManager.Login("Dan", "test");
            Assert.IsTrue(result.Success);
            Session session1 = result.Data as Session;
            Assert.IsNotNull(session1);
            Assert.IsEqual("Dan", session1.UserInfo.Username);
            Assert.IsFalse(session1.UserInfo.IsAdmin);
            Assert.IsTrue(session1.UserInfo.IsDesigner);

            // Try looking up a user named "Joe", using an Dan's session
            // This should not work because Dan is not an administratior
            result = accountManager.Lookup(session1, "Joe");
            Assert.IsFalse(result.Success);

            // Log in as Jane
            result = accountManager.Login("Jane", "test");
            Assert.IsTrue(result.Success);
            Session session2 = result.Data as Session;
            Assert.IsEqual("Jane", session2.UserInfo.Username);
            Assert.IsFalse(session2.UserInfo.IsAdmin);
            Assert.IsFalse(session2.UserInfo.IsDesigner);

            // Try looking up a user named "Joe", using an Mary's session
            // This should not work because Mary is not an administratior
            result = accountManager.Lookup(session2, "Dan");
            Assert.IsFalse(result.Success);
        }

        [Test(7)]
        public void TestGetAllUsers()
        {
            // Get a list of all users, using the admin session
            OperationResult result = accountManager.AllUsers(mySession);
            Assert.IsTrue(result.Success);
            List<UserAccount> users = result.Data as List<UserAccount>;
            Assert.IsNotNull(users);
            Assert.IsTrue(users.Count > 0);

            // Log in as Jane
            result = accountManager.Login("Jane", "test");
            Assert.IsTrue(result.Success);
            Session session2 = result.Data as Session;
            Assert.IsNotNull(session2);
            Assert.IsEqual("Jane", session2.UserInfo.Username);
            Assert.IsFalse(session2.UserInfo.IsAdmin);
            Assert.IsFalse(session2.UserInfo.IsDesigner);

            // Try looking up a user named "Joe", using an Mary's session
            // This should not work because Mary is not an administratior
            result = accountManager.AllUsers(session2);
            Assert.IsFalse(result.Success);
        }

        [Test(8)]
        public void TestAdminUserAccountEdit()
        {
            // TODO
        }

        [Test(9)]
        public void TestAdminUserAccountRemoval()
        {
            // TODO
        }

    }

}
